//! @file   lc_char_traits.h
//! @brief  
//! @author Sayan Chaliha
//! @date   September, 2011
//!
//! 
#ifndef _LC_CHAR_TRAITS_H
#define _LC_CHAR_TRAITS_H

#include "libcyan_internal.h"
#include "lc_types.h"
#include "lc_numeric_limits.h"

namespace cyan {
  template <typename T>
  Bool CYAN_API isNull(T) {
    return false;
  }

  template <typename T>
  Bool CYAN_API isNull(T* t) {
    return t == nullptr;
  }

  template <typename C>
  struct CYAN_API CharTypes {
    typedef UInt32  IntType;
    typedef Size    SizeType;
  };

  template <typename C>
  struct CYAN_API CharTraits {
    typedef C                               ValueType;
    typedef typename CharTypes<C>::IntType  IntType;
    typedef typename CharTypes<C>::SizeType SizeType;

    static void assign(ValueType& c1, const ValueType& c2) {
      c1 = c2;
    }

    static Bool eq(const ValueType& c1, const ValueType& c2) {
      return (c1 == c2);
    }

    static Bool lt(const ValueType& c1, const ValueType& c2) {
      return (c1 < c2);
    }

    static ValueType* assign(ValueType* s, Size n, ValueType a) {
      if (n == 0 || isNull(s))
        return s;

      for (Size i = 0; i < n; ++i)
        s[i] = a;

      return s;
    }

    static Int compare(const ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || s1 == s2)
        return 0;
      else if (isNull(s1) || isNull(s2))
        return NumericLimits<Int>::minimum();

      for (Size i = 0; i < n; ++i) {
        if (lt(s1[i], s2[i]))
          return -1;
        else if (lt(s2[i], s1[i]))
          return 1;
      }
      return 0;
    }

    static Size length(const ValueType* s) {
      Size i = 0;

      if (isNull(s))
        return i;

      while (s[i] != ValueType())
        ++i;

      return i;
    }

    static const ValueType* find(const ValueType* s, Size n,
        const ValueType& c) {
      if (n == 0 || isNull(s))
        return nullptr;

      for (Size i = 0; i < n; ++i)
        if (eq(s[i], c))
          return s + i;

      return nullptr;
    }

    static ValueType* move(ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || isNull(s1) || isNull(s2))
        return s1;

      return static_cast<ValueType*>(::std::memmove(s1, s2,
          n * sizeof (ValueType)));
    }

    static ValueType* copy(ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || isNull(s1) || isNull(s2))
        return s1;

      return static_cast<ValueType*>(::std::memcpy(s1, s2,
          n * sizeof (ValueType)));
    }

    static ValueType toCharType(const IntType& c) {
      return implicit_cast<ValueType>(c);
    }

    static IntType toIntType(const ValueType& c) {
      return implicit_cast<IntType>(c);
    }

    static Bool eqIntType(const ValueType& c1, const IntType& c2) {
      return (c1 == toCharType(c2));
    }

    static IntType eof() {
      return implicit_cast<IntType>(EOF);
    }

    static IntType notEof(const IntType& c) {
      return !eqIntType(c, eof()) ? c : toIntType(ValueType());
    }

    static Int vsnprintf(ValueType*, SizeType, const Char*, va_list) {
      return NumericLimits<Int>::minimum();
    }

    static Bool parseInt32(const ValueType*, Int32&) {
      return false;
    }

    static Bool parseInt64(const ValueType*, Int64&) {
      return false;
    }

    static Bool parseDouble(const ValueType*, Double&) {
      return false;
    }

    static Bool isWhiteSpace(const ValueType&) {
      return false;
    }

    static Bool isAscii(const ValueType&) {
      return false;
    }

    static Char toAscii(const ValueType& c) {
      return Char(c & 0x7F);
    }

    static SizeType toMbs(Char*, const ValueType*, SizeType) {
      return SizeType(-1);
    }

    static SizeType toWcs(WChar*, const ValueType*, SizeType) {
      return SizeType(-1);
    }
  };

  template <>
  struct CYAN_API CharTraits<Char> {
    typedef Char        ValueType;
    typedef Int         IntType;
    typedef Size        SizeType;

    static void assign(ValueType& c1, const ValueType& c2) {
      c1 = c2;
    }

    static Bool eq(const ValueType& c1, const ValueType& c2) {
      return (c1 == c2);
    }

    static Bool lt(const ValueType& c1, const ValueType& c2) {
      return (c1 < c2);
    }

    static ValueType toCharType(const IntType& c) {
      return implicit_cast<ValueType>(c);
    }

    static IntType toIntType(const ValueType& c) {
      return implicit_cast<IntType>(c);
    }

    static Bool eqIntType(const ValueType& c1, const IntType& c2) {
      return (c1 == toCharType(c2));
    }

    static IntType eof() {
      return implicit_cast<IntType>(EOF);
    }

    static IntType notEof(const IntType& c) {
      return !eqIntType(c, eof()) ? c : toIntType(ValueType());
    }

    static ValueType* assign(ValueType* s, Size n, ValueType a) {
      if (n == 0 || isNull(s))
        return s;

      for (Size i = 0; i < n; ++i)
        s[i] = a;

      return s;
    }

    static Int compare(const ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || s1 == s2)
        return 0;
      else if (isNull(s1) || isNull(s2))
        return NumericLimits<Int>::minimum();

      return ::std::memcmp(s1, s2, n);
    }

    static Size length(const ValueType* s) {
      if (isNull(s))
        return 0;

      return ::std::strlen(s);
    }

    static const ValueType* find(const ValueType* s, Size n,
        const ValueType& c) {
      if (n == 0 || isNull(s))
        return nullptr;

      return static_cast<const ValueType*>(::std::memchr(s, c, n));
    }

    static ValueType* move(ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || isNull(s1) || isNull(s2))
        return s1;

      return static_cast<ValueType*>(::std::memmove(s1, s2, n));
    }

    static ValueType* copy(ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || isNull(s1) || isNull(s2))
        return s1;

      return static_cast<ValueType*>(::std::memcpy(s1, s2, n));
    }

    static Int vsnprintf(ValueType* buffer, SizeType length,
        const ValueType* format, va_list args) {
      if (length == 0 || isNull(buffer) || isNull(format))
        return NumericLimits<Int>::minimum();
      return ::vsnprintf(buffer, length, format, args);
    }

    static Bool parseInt32(const ValueType* str, Int32& output) {
      if (isNull(str))
        return false;

      ValueType* endPoint;
      output = Int32(::std::strtol(str, &endPoint, 10));

      if (endPoint != str + CharTraits::length(str))
        return false; 
      return true;
    }

    static Bool parseInt64(const ValueType* str, Int64& output) {
      if (isNull(str))
        return false;

      ValueType* endPoint;
      output = Int64(::std::strtoll(str, &endPoint, 10));

      if (endPoint != str + CharTraits::length(str))
        return false;
      return true;
    }

    static Bool parseDouble(const ValueType* str, Double& output) {
      if (isNull(str))
        return false;

      ValueType* endPoint;
      output = Double(::std::strtod(str, &endPoint));

      if (endPoint != str + CharTraits::length(str))
        return false;
      return true;
    }

    static Bool isWhiteSpace(const ValueType& c) {
      return ::std::isspace(c) != 0;
    }

    static Bool isAscii(const ValueType& c) {
      return ::isascii(c);
    }

    static Char toAscii(const ValueType& c) {
      return CharTraits::toCharType(::toascii(c));
    }

    static SizeType toMbs(Char* mbstr, const ValueType* str, SizeType n) {
      CharTraits::copy(mbstr, str, n);
      return n;
    }

    static SizeType toWcs(WChar* wcstr, const ValueType* str, SizeType n) {
      if (isNull(wcstr) || isNull(str) || n == 0)
        return SizeType(-1);

      return ::std::mbstowcs(wcstr, str, n);
    }
  };

  template <>
  struct CYAN_API CharTraits<WChar> {
    typedef WChar       ValueType;
    typedef WInt        IntType;
    typedef Size        SizeType;

    static void assign(ValueType& c1, const ValueType& c2) {
      c1 = c2;
    }

    static Bool eq(const ValueType& c1, const ValueType& c2) {
      return (c1 == c2);
    }

    static Bool lt(const ValueType& c1, const ValueType& c2) {
      return (c1 < c2);
    }

    static ValueType toCharType(const IntType& c) {
      return implicit_cast<ValueType>(c);
    }

    static IntType toIntType(const ValueType& c) {
      return implicit_cast<IntType>(c);
    }

    static Bool eqIntType(const ValueType& c1, const IntType& c2) {
      return (c1 == toCharType(c2));
    }

#ifdef LC_COMPILER_GNU
//#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif /* LC_COMPILER_GNU */
    static IntType eof() {
      return implicit_cast<IntType>(WEOF);
    }
#ifdef LC_COMPILER_GNU
//#pragma GCC diagnostic pop
#endif /* LC_COMPILER_GNU */

    static IntType notEof(const IntType& c) {
      return !eqIntType(c, eof()) ? c : toIntType(ValueType());
    }

    static ValueType* assign(ValueType* s, Size n, ValueType a) {
      if (n == 0 || isNull(s))
        return s;

      for (Size i = 0; i < n; ++i)
        s[i] = a;

      return s;
    }

    static Int compare(const ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || s1 == s2)
        return 0;
      else if (isNull(s1) || isNull(s2))
        return NumericLimits<Int>::minimum();

      return ::std::wmemcmp(s1, s2, n);
    }

    static Size length(const ValueType* s) {
      if (isNull(s))
        return 0;

      return ::std::wcslen(s);
    }

    static const ValueType* find(const ValueType* s, Size n,
        const ValueType& c) {
      if (n == 0 || isNull(s))
        return nullptr;

      return implicit_cast<const ValueType*>(::std::wmemchr(s, c, n));
    }

    static ValueType* move(ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || isNull(s1) || isNull(s2))
        return s1;

      return static_cast<ValueType*>(::std::wmemmove(s1, s2, n));
    }

    static ValueType* copy(ValueType* s1, const ValueType* s2, Size n) {
      if (n == 0 || isNull(s1) || isNull(s2))
        return s1;

      return static_cast<ValueType*>(::std::wmemcpy(s1, s2, n));
    }

    static Int vsnprintf(ValueType* buffer, SizeType length,
        const ValueType* format, va_list args) {
      if (isNull(buffer) || isNull(format) || length == 0)
        return NumericLimits<Int>::minimum();
      return ::vswprintf(buffer, length, format, args);
    }

    static Bool parseInt32(const ValueType* str, Int32& output) {
      if (isNull(str))
        return false;

      ValueType* endPoint;
      output = Int32(::std::wcstol(str, &endPoint, 10));

      if (endPoint != str + CharTraits::length(str))
        return false;
      return true;
    }

    static Bool parseInt64(const ValueType* str, Int64& output) {
      if (isNull(str))
        return false;

      ValueType* endPoint;
      output = Int64(::std::wcstoll(str, &endPoint, 10));

      if (endPoint != str + CharTraits::length(str))
        return false;
      return true;
    }

    static Bool parseDouble(const ValueType* str, Double& output) {
      if (isNull(str))
        return false;

      ValueType* endPoint;
      output = Double(::std::wcstod(str, &endPoint));

      if (endPoint != str + CharTraits::length(str))
        return false;
      return true;
    }

    static Bool isWhiteSpace(const ValueType& c) {
      return ::iswspace(c) != 0;
    }

    static Bool isAscii(const ValueType& c) {
      return iswascii(c);
    }

    static Char toAscii(const ValueType& c) {
      return CharTraits::toCharType(::toascii(c));
    }

    static SizeType toMbs(Char* mbstr, const ValueType* str, SizeType n) {
      if (isNull(mbstr) || isNull(str) || n == 0)
        return SizeType(-1);
      return ::std::wcstombs(mbstr, str, n);
    }

    static SizeType toWcs(WChar* wcstr, const ValueType* str, SizeType n) {
      CharTraits::copy(wcstr, str, n);
      return n;
    }
  };
}

#endif /* _LC_CHAR_TRAITS_H */
